# 前端路由

  在 vue 中,我们就知道了前端路由的核心是:改变 URL 时,页面不进行整体的刷新

  并且常见的路由实现方式有:

  • URL 的 hash
  • HMTL5 的 history

# 一、react-router

# 1、安装配置

  目前 react-router (opens new window) 已经更新到了 v6.x 版本,相较于之前的版本发生了较大的变化

  本处的笔记,以 v6.x 版本为主,以对比其与 v4.x/v5.x 版本差异为辅。

  react-router 官方主要提供了下面几个 Package 包供我们使用:

  • react-router
  • react-router-dom
  • react-router-native

安装:

npm install react-router-dom

配置:

// index.js
import { BrowserRouter, HashRouter } from 'react-router-dom'

// ……
root.render(
  <HashRouter>
    <App />
  </HashRouter>
)

# 2、基础示例

  react-router 中的<Link><Route> 类似于 vue-router 中的<router-link><router-view>

router 配置

  在 react-router 中,router 规则是直接放在 App.jsx 中的

<div className="content">
  {
    <Routes>
      <Route path="/home" element={<Home />} />
      <Route path="/cart" element={<Cart />} />
      <Route path="/search" element={<Search />} />
    </Routes>
  }
</div>

  而在 vue-router 中,这些 router 规则是需要抽离出来进行单独管理的

<div class="content">
  <router-view></router-view>
</div>

版本差异

  在 v5.x 版本中,Route 最外层包裹的是 <Switch> 组件而不是 <Routes> 组件

  在 v5.x 版本中,使用的是 component 属性(和 vue-router 保持一致),而不是 element 属性

import { Route, Routes, Link } from 'react-router-dom'

class App extends React.PureComponent {
  render() {
    return (
      <div className="app">
        {/* …… */}
        <div className="nav-content">
          <div className="nav">
            <Link to="/home">首页</Link>
            <Link to="/cart">购物车</Link>
            <Link to="/search">搜索</Link>
          </div>
          <div className="content">
            {/* 映射关系:path => Component */}
            {
              <Routes>
                <Route path="/home" element={<Home />} />
                <Route path="/cart" element={<Cart />} />
                <Route path="/search" element={<Search />} />
              </Routes>
            }
          </div>
        </div>
        {/* …… */}
      </div>
    )
  }
}
拓展:replace 属性 与 NavLink 组件

  下面其实是 react-router 中一些和 vue-router 相似的功能

  • 去除历史记录

replace 属性:防止产生历史记录

// vue-router 中
<router-link replace to="/home">首页</router-link>

// react-router 中
<Link replace={true} to="/home">
  首页
</Link>
  • nav 选中样式

核心就是动态添加一个'active'的 class 类名

// vue-router 中
// 详见:https://v3.router.vuejs.org/zh/api/#active-class
<router-link active-class="active" to="/home">首页</router-link>

// react-router 中
// 详见:https://reactrouter.com/en/main/components/nav-link
<NavLink to="/home" >首页</NavLink>

# 3、重定向

  直接使用<Navigate> (opens new window)组件即可

版本差异

  在 v5.x 版本中,使用的是 <Redirect> (opens new window),而不是这里的<Navigate>

  • 场景 1
<div>
  <h2>Login Page</h2>
  {isLogin ? <Navigate to="/home" /> : <button>登录</button>}
</div>
  • 场景 2
<div className="content">
  {
    <Routes>
      {/* 重定向1 ✖ */}
      {/* <Route path="/" element={<Home />} /> */}

      {/* 重定向2 ✔ */}
      <Route path="/" element={<Navigate to="/home" />} />

      <Route path="/home" element={<Home />} />
      <Route path="/cart" element={<Cart />} />
      <Route path="/search" element={<Search />} />
    </Routes>
  }
</div>
拓展:NotFound 页面
<Route path="*" element={<NotFound />} />

# 二、Route 相关

  前面我们对 Route 组件的使用仅限于 path 属性和 element 属性,其实 Route 组件还有很多其他的用途:

官方介绍:Route (opens new window)

Route 事件的所有可用 props
interface RouteObject {
  path?: string
  index?: boolean
  children?: React.ReactNode
  caseSensitive?: boolean
  id?: string
  loader?: LoaderFunction
  action?: ActionFunction
  element?: React.ReactNode | null
  Component?: React.ComponentType | null
  errorElement?: React.ReactNode | null
  ErrorBoundary?: React.ComponentType | null
  handle?: RouteObject['handle']
  shouldRevalidate?: ShouldRevalidateFunction
  lazy?: LazyRouteFunction<RouteObject>
}

# 1、路由嵌套

  官方文档中将其叫做:layout route。就是将子路由直接嵌套在<Route></Route>内。

  v6.x 中使用 <Route></Route> + <Outlet>这种写法的,可以防止 Route 过于分散

版本差异

  在 v5.x 中是没有<Outlet>的,所以发生路由嵌套时,正常写就可以了

// App.jsx

<div className="app">
  {/* …… */}
  <div className="nav-content">
    <div className="nav">
      <Link to="/home">首页</Link>
      <Link to="/cart">购物车</Link>
      <Link to="/search">搜索</Link>
    </div>
    <div className="content">
      {
        <Routes>
          <Route path="/" element={<Navigate to="/home" />} /> {/* 重定向 */}
          {/* 若存在子路由 */}
          <Route path="/home" element={<Home />}>
            <Route path="/home" element={<Navigate to="/home/part1" />} /> {/* 重定向 */}
            <Route path="/home/part1" element={<Part1 />} />
            <Route path="/home/part2" element={<Part2 />} />
          </Route>
          <Route path="/cart" element={<Cart />} />
          <Route path="/search" element={<Search />} />
        </Routes>
      }
    </div>
  </div>
  {/* …… */}
</div>
  • home 组件
<div>
  {/* …… */}
  <div className="nav-content">
    <div className="nav">
      <Link to="/home/part1">推荐</Link>
      <Link to="/home/part2">排行榜</Link>
    </div>
    <div className="content">
      {/* 占位 */}
      <OutLet />
    </div>
  </div>
  {/* …… */}
</div>

# 2、router 配置

  在 react-router@6 中,我们可以直接使用useRoutes(routes)将 route 提取到单独的文件中进行配置:

版本差异

  在 react-router@5 中,同样可以可对 router 规则进行单独配置,只不过需要借助 react-router-config (opens new window)

  • App.jsx
import { useRoutes } from 'react-router-dom'
import routes from '@/router/index.js'

class App extends React.PureComponent {
  render() {
    return (
      <div className="app">
        {/* …… */}
        <div className="nav-content">
          <div className="nav">
            <Link to="/home">首页</Link>
            <Link to="/cart">购物车</Link>
            <Link to="/search">搜索</Link>
          </div>
          <div className="content">
            {/* 映射关系:path => Component */}
            {useRoutes(routes)}
            {/* {
        <Routes>
          <Route path="/" element={<Navigate to="/home" />} />
          <Route path="/home" element={<Home />}>
            <Route path="/home" element={<Navigate to="/home/part1" />} />
            <Route path="/home/part1" element={<Part1 />} />
            <Route path="/home/part2" element={<Part2 />} />
          </Route>
          <Route path="/cart" element={<Cart />} />
          <Route path="/search" element={<Search />} />
        </Routes>
      } */}
          </div>
        </div>
        {/* …… */}
      </div>
    )
  }
}
  • @/router/index.js
const routes = [
  { path: '/', element: <Navigate to="/home" /> },
  {
    path: '/home',
    element: <Home />,
    children: [
      { path: '/home', element: <Navigate to="/home/part1" /> },
      { path: '/home/part1', element: <Part1 /> },
      { path: '/home/part2', element: <Part2 /> }
    ]
  },
  { path: '/cart', element: <Cart /> },
  { path: '/search', element: <Search /> }
]

export default routes

# 3、懒加载

  和 vue-react 应用,react-router 同样可以通过懒加载实现分包处理:

// 以前
import Home from '../pages/Home.jsx'

// 现在
const Home = React.lazy(() => import('../pages/Home.jsx'))

  这样的话,开发中我们可以这样使用:

// 根index.js
root.render(
  <Suspense fallback={<div>Loading</div>}>
    <HashRouter>
      <App />
    </HashRouter>
  </Suspense>
)
// @/router/index.js

const routes = [
  { path: '/cart', element: React.lazy(() => import('../pages/Cart.jsx')) }
  //
]
更新于 : 8/7/2024, 2:16:31 PM